#!/usr/bin/env python
#
# Copyright 2010 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Handles OAuth authorization of users."""

__author__ = 'Vic Fryzel <vicfryzel@google.com>'

import logging
import gdata.docs.client
import gdata.gauth
from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app


class OAuthTokenHandler:
  CONSUMER_KEY = 'anonymous'
  CONSUMER_SECRET = 'anonymous'

  # Purposely avoid using spreadsheets or content scopes, as this app should
  # only be able to consume the documents list feed and upload items.
  # In most other applications, you'd also add the scopes:
  #   https://spreadsheets.google.com/feeds/
  #   https://docs.googleusercontent.com/
  SCOPES = ['https://docs.google.com/feeds/',]

  def GetRequestKey(self):
    """Return the storage key for getting/setting the request token."""
    return users.get_current_user().user_id()

  def GetAccessKey(self):
    """Return the storage key for getting/setting the access token."""
    return '%s-access' % users.get_current_user().user_id()

  def GetAuthorizedClient(self):
    """Return an authorized DocsClient for making requests to the API.

    Also tests if the current access token is valid or not.
    """
    access_token = gdata.gauth.AeLoad(self.GetAccessKey())
    if access_token is None:
      return None
    client = gdata.docs.client.DocsClient(source='gcategorically')
    client.auth_token = access_token
    try:
      # Perform a test API call and clean up if it fails
      client.GetResources(uri='/feeds/default/private/full?max-results=1')
    except:
      gdata.gauth.AeDelete(self.GetRequestKey())
      gdata.gauth.AeDelete(self.GetAccessKey())
      return None
    return client


class OAuthHandler(webapp.RequestHandler):
  def get(self):
    """Perform the OAuth dance for this application.

    Path A: If we already have a stored access token for this user:
      1. Redirect the user to the main part of the application

    Path B: If a user comes in with no token:
      1. Fetch a request token from Google
      2. Store the request token temporarily
      3. Redirect the user to Google to authorize the request token
      4. Wait for Google to redirect the user back here with an authorized token

    Path C: If a user comes in with a token:
      1. Assume token is an authorized request token
      2. Send authorized request token to Google, and upgrade to access token
      3. Store access token so that future requests don't need to re-auth
    """
    th = OAuthTokenHandler()

    # Path A
    access_token = gdata.gauth.AeLoad(th.GetAccessKey())
    if access_token is not None:
      self.redirect('/')
      return

    client = gdata.docs.client.DocsClient(source='gcategorically')
    request_token = gdata.gauth.AeLoad(th.GetRequestKey())
    oauth_callback_url = 'http://%s/auth' % self.request.host

    # Path B
    if not self.request.get('oauth_token') or not request_token.token:
      logging.info('No token found in URL')
      oauth_callback_url = 'http://%s/auth' % self.request.host
      request_token = client.GetOAuthToken(
          th.SCOPES, oauth_callback_url, th.CONSUMER_KEY,
          consumer_secret=th.CONSUMER_SECRET)
      gdata.gauth.AeSave(request_token, th.GetRequestKey())
      self.redirect(str(request_token.generate_authorization_url()))
      return

    # Path C
    else:
      authorized_request_token = gdata.gauth.AuthorizeRequestToken(
          request_token, self.request.uri)
      access_token = client.GetAccessToken(authorized_request_token)
      gdata.gauth.AeSave(access_token, th.GetAccessKey())
      self.redirect('/')


application = webapp.WSGIApplication(
    [('/auth', OAuthHandler)],
    debug=True)


def main():
  """Run the OAuthHandler application."""
  run_wsgi_app(application)


if __name__ == "__main__":
    main()
